package com.jason.fingerprint.common;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;

/**
 * @ClassName: SoundRecorder
 * @Description: 录音工具类
 * @Author: John qiangping.zhu@gmail.com
 * @Date: 2014年11月06日
 * 
 */
public class SoundRecorder implements OnCompletionListener, OnErrorListener {
	static final String TAG = "Recorder";
	static final String SAMPLE_PREFIX = "record";
	static final String SAMPLE_PATH_KEY = "sample_path";
	static final String SAMPLE_LENGTH_KEY = "sample_length";
	static final String DATE_FORMAT = "yyyyMMddHHmmss";

	public static final int STATE_IDLE = 1; // 未录制
	public static final int STATE_RECORDING = 2; // 录制
	public static final int STATE_PAUSE_RECORDING = 3; // 暂停录制
	public static final int STATE_PLAYING = 4; // 播放
	public static final int STATE_PAUSE_PLAYING = 5; // 暂停播放
	public static final int STATE_STOP_RECORDING = 6; // 停止录制

	private int mState = STATE_IDLE;

	public static final int NO_ERROR = 0;
	public static final int SDCARD_ACCESS_ERROR = 1;
	public static final int INTERNAL_ERROR = 2;
	public static final int IN_CALL_RECORD_ERROR = 3;
	public static final int UNSUPPORTED_FORMAT = 4;

	public int mChannels = 0;
	public int mSamplingRate = 0;
	public String mStoragePath;
	public String mTime;

	public interface OnStateChangedListener {
		public void onStateChanged(int state);

		public void onError(int error);
	}

	List<OnStateChangedListener> mOnStateChangedListener = new ArrayList<OnStateChangedListener>();

	long mSampleStart = 0;
	long mSampleLength = 0;
	File sampleDir; // 录音目录
	File mSampleFile = null; // 录音文件
	private String mTypeExtension; // 录音类型

	MediaRecorder mRecorder = null;
	MediaPlayer mPlayer = null;

	public void saveState(Bundle recorderState) {
		recorderState.putString(SAMPLE_PATH_KEY, mSampleFile.getAbsolutePath());
		recorderState.putLong(SAMPLE_LENGTH_KEY, mSampleLength);
	}

	public int getMaxAmplitude() {
		if (mState != STATE_RECORDING)
			return 0;
		return mRecorder.getMaxAmplitude();
	}

	public void restoreState(Bundle recorderState) {
		String samplePath = recorderState.getString(SAMPLE_PATH_KEY);
		if (samplePath == null)
			return;
		long sampleLength = recorderState.getLong(SAMPLE_LENGTH_KEY, -1);
		if (sampleLength == -1)
			return;

		File file = new File(samplePath);
		if (!file.exists())
			return;
		if (mSampleFile != null
				&& mSampleFile.getAbsolutePath().compareTo(
						file.getAbsolutePath()) == 0)
			return;

		delete();
		mSampleFile = file;
		mSampleLength = sampleLength;
		signalStateChanged(STATE_IDLE);
	}

	public void setOnStateChangedListener(OnStateChangedListener listener) {
		mOnStateChangedListener.add(listener);
	}

	public void removeOnStateChangedListener(OnStateChangedListener listener) {
		mOnStateChangedListener.remove(listener);
	}

	public void setChannels(int nChannelsCount) {
		mChannels = nChannelsCount;
	}

	public void setSamplingRate(int samplingRate) {
		mSamplingRate = samplingRate;
	}

	public int getState() {
		return mState;
	}

	public int progress() {
		if (mState == STATE_RECORDING) {
			return (int) ((mSampleLength + (System.currentTimeMillis() - mSampleStart)) / 1000);
		} else if (mState == STATE_PLAYING) {
			return (int) ((System.currentTimeMillis() - mSampleStart) / 1000);
		}
		return 0;
	}

	public int sampleLength() {
		return (int) (mSampleLength / 1000);
	}

	public File sampleFile() {
		return mSampleFile;
	}

	public String getStartRecordingTime() {
		return mTime;
	}

	public File getSampleDir() {
		return sampleDir;
	}

	public String getType() {
		return mTypeExtension;
	}

	/**
	 * Resets the recorder state. If a sample was recorded, the file is deleted.
	 */
	public void delete() {
		stop();

		if (mSampleFile != null)
			mSampleFile.delete();

		mSampleFile = null;
		mSampleLength = 0;
		setState(STATE_IDLE);
		signalStateChanged(STATE_IDLE);
	}

	/**
	 * Resets the recorder state. If a sample was recorded, the file is left on
	 * disk and will be reused for a new recording.
	 */
	public void clear() {
		stop();

		mSampleFile = null;
		mSampleLength = 0;
		setState(STATE_IDLE);
		signalStateChanged(STATE_IDLE);
	}

	public void startRecording(int outputfileformat, String extension,
			Context context, int audiosourcetype, int codectype) {
		stop();

		if (mSampleFile != null) {
			delete();
		}

		if(TextUtils.isEmpty(mStoragePath)) {
			sampleDir = FileHelper.getFileFromName(context, FileHelper.AUDIOS_FILE_NAME);
		} else {
			sampleDir = new File(mStoragePath);
		}

		if (!sampleDir.exists()) {
			sampleDir.mkdirs();
		}

		try {
			SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
					DATE_FORMAT);
			mTime = simpleDateFormat.format(new Date(System
					.currentTimeMillis()));
			mTypeExtension = extension;

			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.append(SAMPLE_PREFIX).append(mTime).append(".tmp");

			String name = stringBuilder.toString();
			Log.d(TAG, "startRecording sampleDir : " + sampleDir + " , name : "
					+ name);
			mSampleFile = new File(sampleDir, name);

			if (!mSampleFile.createNewFile()) {
				mSampleFile = File.createTempFile(SAMPLE_PREFIX, extension,
						sampleDir);
			}
		} catch (IOException e) {
			Log.e(TAG, "create SampleFile error : IOException");
			e.printStackTrace();
			setError(SDCARD_ACCESS_ERROR);
			return;
		}

		mRecorder = new MediaRecorder();
		mRecorder.setAudioSource(audiosourcetype);
		// set channel for surround sound recording.
		if (mChannels > 0) {
			mRecorder.setAudioChannels(mChannels);
		}
		if (mSamplingRate > 0) {
			mRecorder.setAudioSamplingRate(mSamplingRate);
		}

		mRecorder.setOutputFormat(outputfileformat);

		try {
			mRecorder.setAudioEncoder(codectype);
		} catch (RuntimeException exception) {
			setError(UNSUPPORTED_FORMAT);
			mRecorder.reset();
			mRecorder.release();
			if (mSampleFile != null)
				mSampleFile.delete();
			mSampleFile = null;
			mSampleLength = 0;
			mRecorder = null;
			return;
		}

		mRecorder.setOutputFile(mSampleFile.getAbsolutePath());

		// Handle IOException
		try {
			mRecorder.prepare();
		} catch (IOException exception) {
			setError(INTERNAL_ERROR);
			Log.e(TAG, "Prepare Failed : IOException");
			exception.printStackTrace();
			mRecorder.reset();
			mRecorder.release();
			if (mSampleFile != null)
				mSampleFile.delete();
			mSampleFile = null;
			mSampleLength = 0;
			mRecorder = null;
			return;
		}
		// Handle RuntimeException if the recording couldn't start
		Log.e(TAG, "audiosourcetype " + audiosourcetype);
		try {
			mRecorder.start();
		} catch (RuntimeException exception) {
			AudioManager audioMngr = (AudioManager) context
					.getSystemService(Context.AUDIO_SERVICE);
			boolean isInCall = ((audioMngr.getMode() == AudioManager.MODE_IN_CALL) || (audioMngr
					.getMode() == AudioManager.MODE_IN_COMMUNICATION));
			if (isInCall) {
				Log.e(TAG, "Start Failed : IN_CALL_RECORD_ERROR");
				exception.printStackTrace();
				setError(IN_CALL_RECORD_ERROR);
			} else {
				Log.e(TAG, "Start Failed : INTERNAL_ERROR");
				exception.printStackTrace();
				setError(INTERNAL_ERROR);
			}
			mRecorder.reset();
			mRecorder.release();
			mRecorder = null;
			return;
		}
		mSampleStart = System.currentTimeMillis();
		setState(STATE_RECORDING);
	}

	public void renameSampleFile(String name) {
		if (sampleDir.exists()) {
			File newName = new File(sampleDir, name);
			mSampleFile.renameTo(newName);
		}
	}

//	public void pauseRecording() {
//		if (mRecorder == null) {
//			return;
//		}
//		try {
//			mRecorder.pause();
//		} catch (RuntimeException exception) {
//			setError(INTERNAL_ERROR);
//			Log.e(TAG, "Pause Failed : RuntimeException");
//			exception.printStackTrace();
//		} catch (NoSuchMethodError exception) {
//			setError(INTERNAL_ERROR);
//			Log.e(TAG, "Pause Failed : NoSuchMethodError");
//			exception.printStackTrace();
//		}
//		mSampleLength = mSampleLength
//				+ (System.currentTimeMillis() - mSampleStart);
//		setState(STATE_PAUSE_RECORDING);
//	}

	public void resumeRecording() {
		if (mRecorder == null) {
			return;
		}
		try {
			mRecorder.start();
		} catch (RuntimeException exception) {
			setError(INTERNAL_ERROR);
			Log.e(TAG, "Resume Failed : RuntimeException");
			exception.printStackTrace();
		}
		mSampleStart = System.currentTimeMillis();
		setState(STATE_RECORDING);
	}

	public void stopRecording() {
		if (mRecorder == null)
			return;
		try {
			mRecorder.stop();
		} catch (RuntimeException exception) {
			setError(INTERNAL_ERROR);
			Log.e(TAG, "Stop Failed : RuntimeException");
			exception.printStackTrace();
		}
		mRecorder.reset();
		mRecorder.release();
		mRecorder = null;
		mChannels = 0;
		if (mState == STATE_RECORDING) {
			mSampleLength = mSampleLength
					+ (System.currentTimeMillis() - mSampleStart);
		}
		setState(STATE_STOP_RECORDING);
	}

	public void startPlayback() {
		stop();

		mPlayer = new MediaPlayer();
		try {
			mPlayer.setDataSource(mSampleFile.getAbsolutePath());
			mPlayer.setOnCompletionListener(this);
			mPlayer.setOnErrorListener(this);
			mPlayer.prepare();
			mPlayer.start();
		} catch (IllegalArgumentException e) {
			setError(INTERNAL_ERROR);
			Log.e(TAG, "start Failed : IllegalArgumentException");
			e.printStackTrace();
			mPlayer = null;
			return;
		} catch (IOException e) {
			setError(SDCARD_ACCESS_ERROR);
			mPlayer = null;
			return;
		}

		mSampleStart = System.currentTimeMillis();
		setState(STATE_PLAYING);
	}

	public void stopPlayback() {
		if (mPlayer == null) // we were not in playback
			return;

		mPlayer.stop();
		mPlayer.release();
		mPlayer = null;
		setState(STATE_IDLE);
	}

	public void stop() {
		stopRecording();
		// stopPlayback();
	}

	public boolean onError(MediaPlayer mp, int what, int extra) {
		stop();
		setError(SDCARD_ACCESS_ERROR);
		return true;
	}

	public void onCompletion(MediaPlayer mp) {
		stop();
	}

	private void setState(int state) {
		if (state == mState)
			return;

		mState = state;
		signalStateChanged(mState);
	}

	private void signalStateChanged(int state) {
		for (OnStateChangedListener lister : mOnStateChangedListener) {
			lister.onStateChanged(state);
		}
	}

	private void setError(int error) {
		for (OnStateChangedListener lister : mOnStateChangedListener) {
			lister.onError(error);
		}
	}

	public void setStoragePath(String path) {
		mStoragePath = path;
	}
}
